package studio.raptor.databus.graph.common;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.common.graph.GraphBuilder;
import com.google.common.graph.MutableGraph;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Objects;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import studio.raptor.databus.graph.domain.EventNode;

/**
 * 事件在内存中的图操作
 *
 * @author jack
 * @since 1.0
 */
public class DirectGraph {

  private static final Logger log = LoggerFactory.getLogger(DirectGraph.class);

  //根节点，直接与root关联的节点可以被处理
  private static final EventNode EVENT_ROOT = new EventNode("0", "w");

  MutableGraph<EventNode> eventGraph;


  public DirectGraph() {
    eventGraph = GraphBuilder.directed().allowsSelfLoops(false).build();
  }

  public DirectGraph(int expectedNodeCount) {
    eventGraph = GraphBuilder.directed().allowsSelfLoops(false).expectedNodeCount(expectedNodeCount)
        .build();
  }



  /**
   * 判断当前节点在图中是否存在
   *
   * @param eventId 事件id
   * @return true存在，false不存在
   */
  public boolean containEvent(String eventId) {
    return eventGraph.nodes().contains(new EventNode(eventId));
  }


  /**
   * 获取指定状态的根节点下{limit}个节点
   *
   * @param status 节点状态
   * @param limit 获取最大节点数量
   */
  public synchronized Set<String> fetchNodes(int limit, String status) {
    Objects.requireNonNull(status);

    Set<EventNode> childNodes = eventGraph.successors(EVENT_ROOT);
    Set<String> eventNodes = new HashSet<>(childNodes.size());
    for (EventNode eventNode : childNodes) {
      if (status.equals(eventNode.getStatus())) {
        eventNodes.add(eventNode.getEventId());
      }
    }
    if (limit > eventNodes.size()) {
      return eventNodes;
    }
    return ImmutableSet.copyOf(Iterables.limit(eventNodes, limit));
  }

  /**
   * 新增节点，与root节点建立关联
   *
   * @param node 新增节点
   * @param status 节点状态
   * @return true 新增成功 false新增失败
   */
  public synchronized boolean addNode(String node, String status) {
    return eventGraph.putEdge(EVENT_ROOT, new EventNode(node, status));
  }

  public boolean addRootNode() {
    return eventGraph.addNode(EVENT_ROOT);
  }


  /**
   * 新增节点，并与父节点建立关系
   *
   * @param node 关联节点
   * @param parentNode 父节点
   * @return true 新增节点成功
   */
  public synchronized boolean addEdge(EventNode parentNode, EventNode node) {

    boolean addSuccess = eventGraph.addNode(node);
    Set<EventNode> predecessors = eventGraph.predecessors(node);

    //节点在图中已存在
    if (!addSuccess && predecessors.contains(EVENT_ROOT) && predecessors.size() > 1) {
      eventGraph.removeEdge(EVENT_ROOT, node);
    }
    return eventGraph.putEdge(parentNode, node);
  }

  /**
   * 新建节点，并与多个父节点建立关系
   * 如果节点已存在，说明已经建立关系，则不与父节点建立关系
   *
   * @param node 关联节点
   * @param status 节点状态
   * @param parentNodes 被关联的父节点集合
   * @return 与当前节点成功建立关联关系的父节点
   */
  public synchronized Set<String> putEdges(String node, String status, Set<String> parentNodes) {
    Set<String> result = Sets.newHashSet(parentNodes);
    for (Iterator<String> ite = parentNodes.iterator(); ite.hasNext(); ) {
      String parentNode = ite.next();
      boolean addSuccess = addEdge(new EventNode(parentNode), new EventNode(node, status));
      if (!addSuccess) {
        result.remove(parentNode);
      }
    }
    return result;
  }


  /**
   * 删除节点，并对删除节点的子节点重新建立关联关系，节点存在两种情况
   * 1.不存在父节点，则直接与root建立关系
   * 2.存在父节点，则不处理
   * @param  eventId 事件id
   */
  public synchronized Set<String> deleteNode(String eventId) {
    EventNode node = new EventNode(eventId);
    if (!eventGraph.nodes().contains(node)) {
      return null;
    }
    Set<String> result = new HashSet<>();
    Set<EventNode> childNodes = eventGraph.successors(node);
    eventGraph.removeNode(node);
    for (Iterator<EventNode> ite = childNodes.iterator(); ite.hasNext(); ) {
      EventNode childNode = ite.next();
      if (eventGraph.inDegree(childNode) == 0) {
        eventGraph.putEdge(EVENT_ROOT, childNode);
        result.add(childNode.getEventId());
      }
    }

    return result;
  }


  public synchronized void updateEventNodeStatus(String eventId, String status) {
    Set<EventNode> allNodes = eventGraph.nodes();
    for (EventNode eventNode : allNodes) {
      if (eventId.equals(eventNode.getEventId())) {
        eventNode.setStatus(status);
      }
    }
  }

  public MutableGraph<EventNode> getEventGraph() {
    return eventGraph;
  }
}
